package ${model.packageFullName};

import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.http.HttpHeaders;
import org.springframework.test.context.TestConstructor;
import org.springframework.util.Assert;
import org.springframework.security.test.context.support.WithMockUser;
import java.util.Arrays;
import java.util.UUID;

import ace.cmp.id.generator.api.LongIdGenerator;
import ace.cmp.jmockdata.core.impl.util.JMockDataUtils;
import ace.cmp.json.api.JsonService;
import ace.cmp.security.oauth2.resource.server.test.service.MockTokenIntrospectService;
import ace.cmp.util.AceAssertUtils;

import ${model.baseTestClassFullName};
import ${model.entityClassFullName};
import ${model.entityRepositoryClassFullName};

/**
* ${model.entityRemark} graphql admin controller unit test.
*
* @author Caspar
*/
@Slf4j
@AllArgsConstructor
public class ${model.className} extends BaseTest {
    private final HttpGraphQlTester graphQlTester;
    private final MockTokenIntrospectService mockTokenIntrospectService;
    private final LongIdGenerator idGenerator;
    private final JMockDataUtils jMockDataUtils;
    private final JsonService jsonService;

    private final ${model.entityRepositoryClassName} repository;
    private final String methodGet = "${model.graphqlMethodGet}";
    private final String methodList = "${model.graphqlMethodList}";
    private final String methodSearch = "${model.graphqlMethodSearch}";
    private final String methodAdd = "${model.graphqlMethodAdd}";
    private final String methodSave = "${model.graphqlMethodSave}";
    private final String methodModify = "${model.graphqlMethodModify}";
    private final String methodRemove = "${model.graphqlMethodRemove}";
    private final String methodRemoveLogic = "${model.graphqlMethodRemoveLogic}";
    private final Class<${model.entityIdType}> idClass = ${model.entityIdType}.class;
    private final Class<${model.entityClassName}> inputClass = ${model.entityClassName}.class;
    private static final String readPermission = "${model.securityPermissionPrefix}_read";
    private static final String writePermission = "${model.securityPermissionPrefix}_write";
    private static final String removePermission = "${model.securityPermissionPrefix}_remove";

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_get() {
        ${model.entityIdType} id = this.addNew();
        graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodGet)
                .variable("id", id)
                .execute()
                .errors()
                .verify()
                .path("model.id")
                .entity(idClass)
                .isEqualTo(id);
    }

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_list() {
        ${model.entityIdType} id = this.addNew();
        graphQlTester.mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodList)
                .variable("ids", id)
                .execute()
                .errors()
                .verify()
                .path("model[0].id")
                .entity(idClass)
                .isEqualTo(id);
    }

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_search() {
        this.addNew();
        this.addNew();
        graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodSearch)
                .variable("query", "")
                .variable("page", 0)
                .variable("size", 2)
                .execute()
                .errors()
                .verify()
                .path("data.model.numberOfElements")
                .entity(Long.class)
                .isEqualTo(2L)
                .path("data.model.content[0].id")
                .hasValue();

    }

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_add() {
        ${model.entityClassName} input = jMockDataUtils.mock(inputClass);
        ${model.entityIdType} id = graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodAdd)
                .variable("input", input)
                .execute()
                .errors()
                .verify()
                .path("id")
                .entity(idClass)
                .matches(p -> p != null)
                .get();

        ${model.entityClassName} entity = this.repository.getById(id);
        AceAssertUtils.isFalse(id.equals(entity.getId()));
    }

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_save() {
        ${model.entityClassName} input = jMockDataUtils.mock(inputClass);
        input.setRowVersion(null);
        ${model.entityIdType} id = graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodSave)
                .variable("saveNullFieldFlag", true)
                .variable("input", input)
                .execute()
                .errors()
                .verify()
                .path("id")
                .entity(${model.entityIdType}.class)
                .matches(p -> p != null)
                .get();

        ${model.entityClassName} entity = this.repository.getById(id);
        AceAssertUtils.isFalse(id.equals(entity.getId()));
    }
    @SneakyThrows
    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_modify() {
        ${model.entityClassName} addModel = jMockDataUtils.mock(inputClass);
        this.repository.insert(addModel);
        ${model.entityClassName} input = this.cloneObj(addModel);
        Thread.sleep(512);
        input.setUpdateTime(System.currentTimeMillis());
        ${model.entityIdType} id = graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodModify)
                .variable("input", input)
                .execute()
                .errors()
                .verify()
                .path("id")
                .entity(idClass)
                .matches(p -> p != null)
                .get();

        ${model.entityClassName} modifyModel = this.repository.getById(id);

        AceAssertUtils.isTrue(modifyModel.getUpdateTime().equals(addModel.getUpdateTime()));
        AceAssertUtils.isFalse(modifyModel.getUpdateTime().equals(input.getUpdateTime()));
    }

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_remove() {
        ${model.entityIdType} id = this.addNew();
        Boolean success = graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodRemove)
                .variable("ids", Arrays.asList(id))
                .execute()
                .errors()
                .verify()
                .path("success")
                .entity(Boolean.class)
                .matches(p -> p)
                .get();

        AceAssertUtils.isFalse(success);
    }

    @WithMockUser(authorities = {readPermission,writePermission,removePermission})
    @Test
    public void test_removeLogicByIdIn() {
        ${model.entityIdType} id = this.addNew();
        Boolean success = graphQlTester
                .mutate()
                .webTestClient(builder -> builder.entityExchangeResultConsumer(p -> log.info("{}", p)))
                .build()
                .documentName(methodRemoveLogic)
                .variable("ids", Arrays.asList(id))
                .execute()
                .errors()
                .verify()
                .path("success")
                .entity(Boolean.class)
                .matches(p -> p)
                .get();

        ${model.entityClassName} model = this.repository.getById(id);

        Assert.isTrue(success);
        Assert.isTrue(model != null);
        Assert.isTrue(model.getDeleteFlag().equals(id));
    }

    private ${model.entityClassName} cloneObj(${model.entityClassName} model) {
        String json = this.jsonService.toJson(model);
        return this.jsonService.toObject(json, inputClass);
    }

    private ${model.entityIdType} addNew() {
        ${model.entityClassName} entity = this.jMockDataUtils.mock(inputClass);
        this.repository.insert(entity);
        return entity.getId();
    }
}
